package com.lobaby.tools.common.utils;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.*;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Administrator
 *         Created by Administrator on 2019/11/25 0025.
 */
public class ReflectUtil {
    private static Logger LOGGER = LoggerFactory.getLogger(ReflectUtil.class);


    /**
     * 创建实例
     *
     * @param className
     *            类名
     * @param constructorArgs
     *            构造函数的参数
     * @return 新建的实例
     * @throws Exception
     */
    public static Object newInstance(String className,
                                     Object... constructorArgs)
    {
        try
        {
            Class<?> newoneClass = Class.forName(className);
            if (constructorArgs == null)
            {
                Constructor<?> cons = newoneClass.getConstructor();
                return cons.newInstance();
            }

            Class<?>[] argsClass = new Class[constructorArgs.length];
            for (int i = 0, j = constructorArgs.length; i < j; i++)
            {
                if (constructorArgs[i] != null)
                {
                    argsClass[i] = constructorArgs[i].getClass();
                }
                else
                {
                    argsClass[i] = null;
                }
            }
            Constructor<?> cons = newoneClass.getConstructor(argsClass);
            return cons.newInstance(constructorArgs);
        }
        catch (SecurityException e)
        {
            LOGGER.error("",e);
            return null;
        }
        catch (IllegalArgumentException e)
        {
            LOGGER.error("",e);
            return null;
        }
        catch (ClassNotFoundException e)
        {
            LOGGER.error("",e);
            return null;
        }
        catch (NoSuchMethodException e)
        {
            LOGGER.error("",e);
            return null;
        }
        catch (InstantiationException e)
        {
            LOGGER.error("",e);
            return null;
        }
        catch (IllegalAccessException e)
        {
            LOGGER.error("",e);
            return null;
        }
        catch (InvocationTargetException e)
        {
            LOGGER.error("",e);
            return null;
        }
    }

    /**
     * 反射执行get方法
     *
     * @param target
     *            对象
     * @param propertyName
     *            属性名
     * @return
     *
     */
    public static Object invokeGetMethod(Object target, String propertyName)
    {
        if (target != null)
        {
            // 找到对应的get方法
            Method method = ReflectionUtils.findMethod(target.getClass(),
                    getGetMethodName(propertyName));

            return invokeMethod(target, method);
        }
        else
        {
            LOGGER.debug("target is null");
            return null;
        }

    }

    /**
     * 反射执行set方法
     *
     * @param target
     *            对象
     * @param propertyName
     *            属性名
     * @param value
     *            赋值
     *
     */
    public static void invokeSetMethod(Object target, String propertyName,
                                       Object value)
    {
        if (target != null)
        {
            // set方法名
            String methodName = getSetMethodName(propertyName);
            // 找到property
            Field field = ReflectionUtils.findField(target.getClass(),
                    propertyName);
            if (field == null)
            {
                LOGGER.debug("field not found");
                return;
            }
            // 找到对应的set方法
            Method method = ReflectionUtils.findMethod(target.getClass(),
                    methodName, field.getType());

            invokeMethod(target, method, value);
        }
        else
        {
            LOGGER.debug("target is null");
        }
    }

    /**
     * Attempt to find a Method on the supplied target's class with the supplied
     * name and no parameters. Searches all superclasses up to Object. And
     * invoke this method.
     *
     * @param target
     *            目标
     * @param methodName
     *            方法名
     * @param objects
     *            参数
     * @return
     *
     */
    public static Object invokeMethod(Object target, String methodName,
                                      Object... objects)
    {
        Method method = null;
        if (objects != null && objects.length > 0)
        {
            Class<?>[] parameterTypes = new Class[objects.length];
            for (int i = 0; i < objects.length; i++)
            {
                parameterTypes[i] = objects[i].getClass();
            }
            method = ReflectionUtils.findMethod(target.getClass(), methodName,
                    parameterTypes);
        }
        else
        {
            method = ReflectionUtils.findMethod(target.getClass(), methodName);
        }
        return invokeMethod(target, method, objects);
    }

    /**
     * Attempt to find a Method on the supplied class with the supplied name and
     * parameter types. Searches all superclasses up to Object. And invoke this
     * method.
     *
     * @param target
     *            目标
     * @param methodName
     *            方法名
     * @param parameterTypes
     *            参数类型
     * @param parameters
     *            参数
     * @return
     *
     */
    public static Object invokeMethod(Object target, String methodName,
                                      Class<?>[] parameterTypes, Object[] parameters)
    {
        Method method = ReflectionUtils.findMethod(target.getClass(),
                methodName, parameterTypes);
        return invokeMethod(target, method, parameters);
    }

    /**
     * 反射执行方法
     *
     * @param target
     *            对象
     * @param method
     *            方法
     * @param parameters
     *            参数
     *
     */
    public static Object invokeMethod(Object target, Method method,
                                      Object... parameters)
    {
        if (target == null)
        {
            LOGGER.debug("target is null");
            return null;
        }
        if (method == null)
        {
            LOGGER.debug("method is null to " + target.getClass());
            return null;
        }
        if (parameters == null)
        {
            LOGGER.debug("parameter is null");
            return null;
        }
        return ReflectionUtils.invokeMethod(method, target, parameters);
    }

    /**
     * 根据名称找方法
     *
     * @param clazz
     * @param name
     *            方法名
     * @return
     *
     */
    public static Method findMethodByName(Class<?> clazz, String name)
    {
        Method result = null;
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods)
        {
            String mname = method.getName();
            if (mname.equals(name))
            {
                result = method;
                break;
            }
        }
        return result;
    }

    /**
     * 反射匹配方法
     *
     * @param clazz
     * @param name
     * @param paramTypes
     * @return
     *
     */
    public static Method findMethodByParamType(Class<?> clazz, String name,
                                               Class<?>... paramTypes)
    {
        if (paramTypes == null)
        {
            return ReflectionUtils.findMethod(clazz, name);
        }
        else
        {
            return ReflectionUtils.findMethod(clazz, name, paramTypes);
        }
    }

    /**
     * 获得get/set方法
     *
     * @param clazz
     * @param property
     * @return [0]get,[1]set
     *
     */
    public static Method[] findGetAndSetMethod(Class<?> clazz, String property)
    {
        Method[] result = new Method[2];
        // get方法
        String getmethodName = ReflectUtil.getGetMethodName(property);
        Method getMethod = ReflectUtil.findMethodByParamType(clazz,
                getmethodName);
        if (getMethod == null)
        {
            LOGGER.debug("no get method for property " + property);

            result[0] = null;
            result[1] = null;
            return result;
        }
        else
        {
            // get方法返回类型
            Class<?> cls = getMethod.getReturnType();

            // set方法
            String setmethod = ReflectUtil.getSetMethodName(property);
            Method setMethod = ReflectUtil.findMethodByParamType(clazz,
                    setmethod, cls);

            result[0] = getMethod;
            result[1] = setMethod;
            return result;
        }
    }

    /**
     * 属性对应的set方法名字
     *
     * @param propertyName
     *            属性名
     * @return
     *
     */
    public static String getGetMethodName(String propertyName)
    {
        return "get" + StringUtils.capitalize(propertyName);
    }

    /**
     * 属性对应的set方法名字
     *
     * @param propertyName
     *            属性名
     * @return
     *
     */
    public static String getSetMethodName(String propertyName)
    {
        return "set" + StringUtils.capitalize(propertyName);
    }

    /**
     * c是否是szInterface的接口实现类
     *
     * @param c
     * @param szInterface
     * @return
     */
    public static boolean isInterface(Class<?> c, String szInterface)
    {
        Class<?>[] face = c.getInterfaces();
        for (int i = 0, j = face.length; i < j; i++)
        {
            if (face[i].getName().equals(szInterface))
            {
                return true;
            }
            else
            {
                Class<?>[] face1 = face[i].getInterfaces();
                for (int x = 0; x < face1.length; x++)
                {
                    if (face1[x].getName().equals(szInterface))
                    {
                        return true;
                    }
                    else if (isInterface(face1[x], szInterface))
                    {
                        return true;
                    }
                }
            }
        }
        if (null != c.getSuperclass())
        {
            return isInterface(c.getSuperclass(), szInterface);
        }
        return false;
    }

    /**
     * map转对象
     *
     * @param map
     * @param beanClass
     * @return
     * @throws Exception
     */
    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null)
            return null;
        Object obj = beanClass.newInstance();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            fieldInvokeToObject(field,obj,map);
        }
        return obj;
    }
    /**
     * map转对象
     *
     * @param map
     * @param beanClass
     * @return
     * @throws Exception
     */
    public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) throws Exception {
        if (map == null)
            return null;
        T obj = beanClass.newInstance();
        Field[] fields = beanClass.getDeclaredFields();
        for (Field field : fields) {
            fieldInvokeToObject(field,obj,map);
        }
        return obj;
    }
    /**
     * field 反射注入
     *
     * @param field
     * @param obj
     * @param map
     * @throws Exception
     */
    public static void fieldInvokeToObject(Field field,Object obj,Map<String, Object> map) throws Exception{

        int mod = field.getModifiers();
        if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
            return;
        }

        field.setAccessible(true);
        String type = field.getType().toString();
        if(map.get(field.getName())==null){
            return;
        }
        String tempStr = map.get(field.getName())+"";
        if(StringUtils.isNotBlank(tempStr)){
            if(type.toString().equals("class java.lang.String")){
                field.set(obj, map.get(field.getName()));
            }
            else if(type.equals("class java.util.Date")){
                if(StringUtils.isNotBlank(tempStr)){
                    if(isDate(tempStr)){
                        DateFormat df = null;
                        if(tempStr.length()==8){
                            df = new SimpleDateFormat("yyyyMMdd");
                        }else if(tempStr.length()==10){
                            df = new SimpleDateFormat("yyyy-MM-dd");
                        }else if(tempStr.length()>10){
                            df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                        }
                        if(df!=null){
                            Date date = df.parse(tempStr);
                            field.set(obj, date);
                        }
                    }
                }
            }
            else if(type.equals("class java.sql.Timestamp")){
                if(StringUtils.isNotBlank(tempStr)){
                    if(isDate(tempStr)){
                        Timestamp timestamp = Timestamp.valueOf(tempStr);
                        field.set(obj, timestamp);
                    }
                }
                field.set(obj, (Date)map.get(field.getName()));
            } else if(type.equals("class java.lang.Integer")){
                if(StringUtils.isNumeric(tempStr)){
                    field.set(obj,  new Integer(tempStr));
                }
            } else if (type.equals("class java.lang.Float")) {
                field.set(obj, new Float(tempStr));
            }
            else{
                LOGGER.error("不支持的转义类型"+type+"--"+obj+"---"+field.getName());
            }
        }
    }


    /**
     * 是否为日期类型
     *
     * @param strDate
     * @return
     */
    public static boolean isDate(String strDate) {
        Pattern pattern = Pattern
                .compile("^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\\s(((0?[0-9])|([1-2][0-3]))\\:([0-5]?[0-9])((\\s)|(\\:([0-5]?[0-9])))))?$");
        Matcher m = pattern.matcher(strDate);
        if (m.matches()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 不区分大小写
     *
     * @param map
     * @param beanClass
     * @return
     * @throws Exception
     */
    public static Object mapToObjectIngoreCase(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null)
            return null;

        Object obj = beanClass.newInstance();

        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            int mod = field.getModifiers();
            if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
                continue;
            }

            field.setAccessible(true);
            String nameStr = field.getName();
            if(map.get(nameStr)!=null){
                field.set(obj, map.get(nameStr));
                continue;
            }
            nameStr = nameStr.toUpperCase();
            if(map.get(nameStr)!=null){
                field.set(obj, map.get(nameStr));
                continue;
            }
            nameStr = nameStr.toLowerCase();
            if(map.get(nameStr)!=null){
                field.set(obj, map.get(nameStr));
                continue;
            }
        }

        return obj;
    }

    /**
     * map转object
     *
     * @param map
     * @param beanClass
     * @return
     * @throws Exception
     */
    public static Object mapToObjectFromStrings(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null)
            return null;

        Object obj = beanClass.newInstance();

        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            int mod = field.getModifiers();
            if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
                continue;
            }

            field.setAccessible(true);
            if((map.get(field.getName()))!=null&&(map.get(field.getName())).getClass().equals(String[].class)){
                Class<?> fieldType = field.getType();// 变量的数据类型
                String[] strings = (String[]) (map.get(field.getName()));
                if (fieldType.toString().equals("int")) {
                    field.set(obj, Integer.valueOf(strings[0]).intValue());
                    continue;
                }else if (fieldType.toString().equals("class java.sql.Timestamp")){
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
                            "yyyy-MM-dd HH:mm:ss");
                    Date date = simpleDateFormat.parse(strings[0]);
                    field.set(obj, new Timestamp(date.getTime()));
                    continue;
                }else if (fieldType.newInstance() instanceof Date) {
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
                            "yyyy-MM-dd");
                    Date date = simpleDateFormat.parse(strings[0]);
                    LOGGER.debug(""+"********" + strings[0]);
                    LOGGER.debug(""+"+++++++++" + date);
                    field.set(obj, date);
                    continue;
                } else if (fieldType.newInstance() instanceof Integer) {
                    field.set(obj, Integer.valueOf(strings[0]).intValue());
                    continue;
                }else if (fieldType.newInstance() instanceof String) {
                    field.set(obj, strings[0]);
                    continue;
                }else if (fieldType.newInstance() instanceof Boolean) {
                    field.set(obj, (strings[0]==null||"".equals(strings[0])||!"1".equals(strings[0])));
                    continue;
                }
            }else{
                if("boolean".equals(field.getType().toString())){
                }else{
                    field.set(obj, map.get(field.getName()));
                }
            }
        }

        return obj;
    }

    /**
     * 对象转map
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static Map<String, Object> objectToMap(Object obj) throws Exception {
        if(obj == null){
            return null;
        }

        Map<String, Object> map = new HashMap<String, Object>();

        //公有属性
        Field[] fields = obj.getClass().getFields();
        for (Field field : fields) {
            field.setAccessible(true);
            map.put(field.getName(), field.get(obj));
        }
        //私有属性
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            map.put(field.getName(), field.get(obj));
        }
        return map;
    }

    /**
     * 对象转换 去空值
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static Map<String, Object> objectToMapWithoutNull(Object obj) throws Exception {
        if(obj == null){
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();

        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            if(field.get(obj)!=null&&!"null".equals(field.get(obj))){
                map.put(field.getName(), field.get(obj));
            }
        }

        return map;
    }

    /**
     * 对象转换
     *
     * @param valueObj
     * @param targetObj
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Object objectToObject(Object valueObj, Class targetObj) throws Exception {
        if(valueObj == null){
            return null;
        }
        Object returnObj = JSON.parseObject(JSON.toJSONString(valueObj),targetObj);
        return returnObj;
    }

    @SuppressWarnings("rawtypes")
    public static void setFieldValue(Class clazz, String fieldName, Object obj, Object value) {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            LOGGER.error("",e);
        }
    }
    public static Object getFieldValue(Object obj, String fieldName) {
        try {
            if (obj == null) {
                return null;
            }
            if (obj instanceof Map) {
                Map<String, Object> map = (Map<String, Object>) obj;
                return map.get(fieldName);
            }else {
                Field field = obj.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(obj);
            }
        } catch (Exception e) {
            return null;
        }
    }
}
